/*
 * ModuleLauncher.java
 *
 * $Id: ModuleLauncher.java,v 1.7 2009-09-17 12:11:26 mario Exp $
 */
package org.ceteca.explica.core.installer;

import java.io.InputStream;
import org.apache.log4j.Logger;
import org.ceteca.explica.client.ClientContext;
import org.ceteca.explica.client.gui.ExplicaGUIBuilder;
import org.ceteca.explica.client.gui.ExplicaModuleView;
import org.ceteca.explica.core.InterfaceExplica;
import org.ceteca.explica.core.util.LoggerFactory;
import org.ceteca.javaglade.JavaGlade;
import org.eclipse.swt.custom.StackLayout;
import org.eclipse.swt.widgets.Composite;
import org.eclipse.swt.widgets.Control;

/**
 * Generic class that defines the common operations of the main class
 * of each module.
 * <br/>
 * Responsibilities:
 * <ul>
 * 	<li>Provide a method for initializing and configuring a module.</li>
 *  <li>Provide a method for starting a module.</li>
 *  <li>Store the catalog of messages resources for each module.</li>
 *  <li>Keep an instance of the logger for the module.</li>
 *  <li>Keep an instance of the internationalization object for the module.</li>
 * </ul>
 * <br/>
 * @author Mario García García <mario@imagos.es>
 * Copyright (c) 2007 Fundación Centro Tecnolóxico da Carne
 * This program is free software: you can redistribute it and/or modify
 * it under the terms of the GNU General Public License as published by
 * the Free Software Foundation, either version 3 of the License, or
 * (at your option) any later version.
 * <br/>
 * This program is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU General Public License for more details.
 * <br/>
 * You should have received a copy of the GNU General Public License
 * along with this program.  If not, see <http://www.gnu.org/licenses/>.
 */
public class ModuleLauncher {
	/**
	 * Instance of the logging system for the module.
	 */
	private Logger logger;
	/**
	 * Object with all the configuration data for the module.
	 */
	private ModuleOT moduleData;
	/**
	 * Flag to show when the module is ready to be started, that is, the
	 * module has been correctly initialized and configured. If any error
	 * takes places during initialization phase, this flag will be unset.
	 */
	private boolean moduleReady;
	/** Instance of the ExplicaModuleView that will construct the view for
	 * the module. */
	private ExplicaModuleView moduleView;
	/**
	 * Instance of the composite panel that will hold all the module view.
	 * This instance will be the result of the building module view phase.
	 * It will be used for selecting the module stack as the one that is
	 * showed to the user.
	 */
	private Composite modulePanel;

	/**
	 * Constant value with the logging message for an error ocurred during the 
	 * loading of the configuration file of the module.
	 */
	public static final String ERROR_CONFIG_MOD = "Cannot initialize the module. An error has ocurred while reading the configuration file of the module!";
	/**
	 * Constant value with the logging message for an error ocurred during the 
	 * loading of the logging configuration file of the module.
	 */
	public static final String ERROR_CONFIG_LOG = "Cannot initialize the logging system. An error has ocurred while reading the log configuration file of the module!";
	/**
	 * Constant value with the logging message for an error ocurred during the 
	 * loading of the result messages properties file of the module.
	 */
	public static final String ERROR_CONFIG_MSG = "Cannot initialize the result messages catalog. An error has ocurred while reading the messages properties file of the module!";
	/**
	 * Constant value with the logging message for an error ocurred during the 
	 * initialization of the module (the flag moduleReady is unset).
	 */
	public static final String ERROR_START_MOD = "Cannot start the module. The module was not correctly initialized and cannot be started!";
	/**
	 * Constant value with the logging message for an error ocurred during the 
	 * building of the module panel.
	 */
	public static final String ERROR_BUILD_MOD_PANEL = "Cannot start the module. The module view couldn't be built!";
	/**
	 * Constant value with the logging message for an error ocurred during the 
	 * loading of the module's main class.
	 */
	public static final String ERROR_START_NOMAINCLASS = "Cannot start the module. Cannot find the module's main class!";
	/**
	 * Constant value with the logging message for an error ocurred during the 
	 * location of the module's starting method.
	 */
	public static final String ERROR_START_NOMAINMETHOD = "Cannot start the module. The main class doesn't have an implementation of the module's starting method!";
	/**
	 * Constant value with the logging message for an error ocurred during the 
	 * instantiation of the module's main class.
	 */
	public static final String ERROR_START_NOMAININSTANCE = "Cannot start the module. An instance of the module's main class couldn't be created!";
	/**
	 * Constant value with the logging message for starting a new module. 
	 */
	public static final String MODULE_START = "Starting module: %s";
	/**
	 * Constant value with the logging message for reporting an error searching for an undefined entity. 
	 */
	public static final String ENTITY_UNDEFINED = "The entity: %s is not defined or cannot be found!";
	/**
	 * Constant value with the logging message for reporting an error building the catalog of entities
	 * for the module. No entities have been defined. 
	 */
	public static final String ERROR_NO_ENTITIES_DEFINED = "The module %s has no entities defined!";
	
	/**
	 * Constructor of ModuleLauncher class.
	 * @param is InputStream, reading stream to the module configuration file.
	 */
	public ModuleLauncher(InputStream is) {
		super();
		
		ClientContext.getInstance().getLogger().
			debug("Constructor de ModuleLauncher!");
		
		this.moduleView = null;
		this.modulePanel = null;
		
		// Get the configuration values for the module. These values are stored in the config
		// file of the module that can be found packaged in the module JAR.
		this.configModule(is);
	}
	
	/**
	 * Initialize and configure the module. As the result of this operation
	 * the module launcher will be ready to start the module when it's requested.
	 */
	public void initModule() {
		// By default (if no error is produced during configuration phase) the
		// module will be ready for execution.
		this.setModuleReady(true);
		
		ClientContext.getInstance().getLogger().
			info("Inicializando el sistema LOG del modulo: " + this.moduleData.getName() + " --> " + this.moduleData.getLogConfigFile());
		// Configure the logger object for the module.
		ClassLoader cl = ClassLoader.getSystemClassLoader();
		try {
			if (this.moduleData.getLogConfigFile() != null && this.moduleData.getLogConfigFile().length() > 0) {
				ClientContext.getInstance().getLogger().
					debug("LOG PATH: " + cl.getResource(this.moduleData.getLogConfigFile()).getPath());
				LoggerFactory.initLogger(null, cl.getResourceAsStream(this.moduleData.getLogConfigFile()));
				ClientContext.getInstance().getLogger().
					debug("Obteniendo el log para el modulo: " + this.moduleData.getLogCategory());
				this.logger = LoggerFactory.getLogger(this.moduleData.getLogCategory());
			} // if log configuration file is initialized
			else {
				// No logger is specified, so the client general logger will be use
				// for logging the module messages.
				this.logger = ClientContext.getInstance().getLogger(this.moduleData.getLogCategory());
			} // else log configuration file can't be read
		} // try
		catch (Exception ex) {
			ClientContext.getInstance().getLogger().error(ERROR_CONFIG_LOG, ex);
			this.setModuleReady(false);
		}
		
		// If no logger can be found for the category of the module, registry
		// the error in the module initialization
		if (this.logger == null) {
			ClientContext.getInstance().getLogger().error(ERROR_CONFIG_LOG);
			this.setModuleReady(false);
		} // if logger couldn't be initialized
		
		// Configure the message catalog for the module.
		this.logger.info("^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^" + 
				"\nConfigurando catalogo de mensajes para el modulo: " + 
				this.moduleData.getName() + " --> " + this.moduleData.getMsgPropFile() +
				"\n^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^");
		try {
			if (this.moduleData.getMsgPropFile() != null && this.moduleData.getMsgPropFile().length() > 0) {
				this.logger.debug("MSG PROPERTIES PATH: " + cl.getResource(this.moduleData.getMsgPropFile()).getPath());
				InputStream is = cl.getResourceAsStream(this.moduleData.getMsgPropFile());
				if (is == null) {
					if (this.isModuleReady())
						this.logger.error(ERROR_CONFIG_MSG);
					else
						ClientContext.getInstance().getLogger().error(ERROR_CONFIG_MSG);
					
					this.setModuleReady(false);
				} // if messages properties file is null
				else {
					ClientContext.getInstance().addModuleResultMessages(is);
				} // else messages properties file is not null
			} // if messages properties file is initialized
			else {
				if (this.isModuleReady())
					this.logger.error(ERROR_CONFIG_MSG);
				else
					ClientContext.getInstance().getLogger().error(ERROR_CONFIG_MSG);
				
				this.setModuleReady(false);
			} // else messages properties file can't be read
		} // try
		catch (Exception ex) {
			if (this.isModuleReady())
				this.logger.error(ERROR_CONFIG_MSG, ex);
			else {
				ClientContext.getInstance().getLogger().error(ERROR_CONFIG_MSG, ex);
			}
			this.setModuleReady(false);
		} // catch

		if (this.logger != null)
			this.logger.info("Configuradas propiedades para el modulo: " + this.isModuleReady());
		
		// Check if the module has any entities
		if (this.moduleData.getLEntities() == null || this.moduleData.getLEntities().size() <= 0) {
			if (this.isModuleReady())
				this.logger.error(String.format(ERROR_NO_ENTITIES_DEFINED, this.moduleData.getName()));
			else {
				ClientContext.getInstance().getLogger().
					error(String.format(ERROR_NO_ENTITIES_DEFINED, this.moduleData.getName()));
			}
			this.setModuleReady(false);
		} // if no entities defined for the module
	}
	
	/**
	 * Load the configuration file of the module and prepares the
	 * module to be started.
	 * @param is InputStream, reading stream to the module configuration
	 * file.
	 */
	private void configModule(InputStream is) {
		try {
			// Parse the configuration file of the module.
			ModuleParser parser = new ModuleParser(is);
			this.moduleData = parser.getModule();
		}
		catch (Exception ex) {
			ex.printStackTrace();
			System.err.println(ERROR_CONFIG_MOD);
		}
	}

	/**
	 * Inits the execution of the module. Shows the main screen of the module.
	 */
	public void startModule() {
		if (!this.isModuleReady() || this.moduleData == null) {
			if (this.logger != null)
				this.logger.error(ERROR_START_MOD);
			else
				ClientContext.getInstance().getLogger().error(ERROR_START_MOD);
		} // if (module not ready)
		else {
			this.logger.debug(String.format(MODULE_START, this.moduleData.getName()));

			// Show the module contents
			ExplicaModuleView.getInstance().showModule(this.moduleData, this.logger);
			
			// Get the parent panel where the module content is embedded
			Composite parent = (Composite)ExplicaGUIBuilder.getInstance().getGui(InterfaceExplica.EXPLICA_CORE_GLADE_NAME).
					getWidget(InterfaceExplica.EXPLICA_MODULE_PANEL_NAME);
			
			// Redraw the parent of the module panel to show the new contents
			parent.layout(true);
		} // else (module is ready)
	}

	/**
	 * Gets the value of the flag moduleReady.
	 * @return boolean, True if the module was correctly configured
	 * and is ready to be started; False in case any error took place
	 * during the module configuration.
	 */
	public boolean isModuleReady() {
		return moduleReady;
	}

	/**
	 * Sets up the value of the flag moduleReady.
	 * @param moduleReady boolean, True if the module was correctly configured
	 * and is ready to be started; False in case any error took place
	 * during the module configuration.
	 */
	public void setModuleReady(boolean moduleReady) {
		this.moduleReady = moduleReady;
	}

	/**
	 * Gets the logger for the module.
	 * @return Logger, the logger for the module.
	 */
	public Logger getLogger() {
		return logger;
	}

	/**
	 * Gets the configuration of the module.
	 * @return ModuleOT, module configuration data.
	 */
	public ModuleOT getModuleData() {
		return this.moduleData;
	}
	
	/**
	 * Returns tag Id assigned to CVS source file.
	 */
	public static String getRevision() {
		return "$Id: ModuleLauncher.java,v 1.7 2009-09-17 12:11:26 mario Exp $";
	}
}